package win.smartown.android.library.scrollpager;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.LinearLayout;
import android.widget.Scroller;

import win.smartown.android.library.scrollpager.interfaces.Adapter;
import win.smartown.android.library.scrollpager.interfaces.OnPagerChangedListener;
import win.smartown.android.library.scrollpager.interfaces.OnPagerScrollListener;
import win.smartown.android.library.scrollpager.interfaces.OnScrollListener;
import win.smartown.android.library.scrollpager.interfaces.Scrollable;

/**
 * Created by Smartown on 2017/9/25.
 */
public class ScrollPager extends LinearLayout implements OnPagerScrollListener {

    private float mStartY;
    private float mStartScrollY;
    //是系统所能识别的被认为是滑动的最小距离
    private float mScaledTouchSlop;
    private int mCurrentChildIndex;
    private Scroller mScroller;
    private OnPagerChangedListener mOnPagerChangedListener;
    private OnScrollListener mOnScrollListener;

    private Adapter mAdapter;

    public void setAdapter(Adapter adapter) {
        this.mAdapter = adapter;
        useAdapter();
    }

    private void useAdapter() {
        removeAllViews();
        mCurrentChildIndex = 0;
        int pagerCount = mAdapter.getPagerCount();
        for (int i = 0; i < pagerCount; i++) {
            addView(mAdapter.getPager(i).getView());
        }
    }

    public ScrollPager(Context context) {
        super(context);
        init();
    }

    public ScrollPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public ScrollPager(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public ScrollPager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        mScaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        mScroller = new Scroller(getContext());
        setOrientation(VERTICAL);
        removeAllViews();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (getChildCount() > 0) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mStartY = ev.getY();
                    mStartScrollY = getScrollY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    final float deltaY = mStartY - ev.getY();
                    if (deltaY >= mScaledTouchSlop) {
                        //手指向上滑出有效距离
                        final View child = getChildAt(mCurrentChildIndex);
                        if (child instanceof Scrollable) {
                            return ((Scrollable) child).atBottom();
                        }
                    } else if (deltaY <= -mScaledTouchSlop) {
                        //手指向下滑出有效距离
                        final View child = getChildAt(mCurrentChildIndex);
                        if (child instanceof Scrollable) {
                            return ((Scrollable) child).atTop();
                        }
                    }
                    break;
            }
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (getChildCount() > 0 && mScroller.isFinished()) {
            notifyOnScroll();
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mStartY = event.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    scrollBy(0, (int) (mStartY - event.getY()));
                    mStartY = event.getY();
                    break;
                case MotionEvent.ACTION_UP:
                    if (mCurrentChildIndex == 0 && getScrollY() < 0) {
                        scrollToChildAt(0);
                    } else if (mCurrentChildIndex == getChildCount() - 1 && getScrollY() > (getChildCount() - 1) * getHeight()) {
                        scrollToChildAt(getChildCount() - 1);
                    } else {
                        final int distanceToScroll = getHeight() / 4;
                        if (getScrollY() > mStartScrollY) {
                            if (getScrollY() % getHeight() < distanceToScroll) {
                                scrollToChildAt(getScrollY() / getHeight());
                            } else {
                                scrollToChildAt(getScrollY() / getHeight() + 1);
                            }
                        } else {
                            if (getScrollY() % getHeight() < distanceToScroll) {
                                scrollToChildAt(getScrollY() / getHeight() + 1);
                            } else {
                                scrollToChildAt(getScrollY() / getHeight());
                            }
                        }
                    }
                    break;
            }
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            //这里调用View的scrollTo()完成实际的滚动
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            //必须调用该方法，否则不一定能看到滚动效果
            postInvalidate();
        }
        notifyOnScroll();
    }

    @Override
    public void onPagerScroll(Scrollable scrollable, int distance) {
        notifyOnScroll();
    }

    public void scrollToChildAt(int position) {
        mScroller.startScroll(0, getScrollY(), 0, position * getHeight() - getScrollY(), 500);
        postInvalidate();
        mCurrentChildIndex = position;
        if (mOnPagerChangedListener != null) {
            mOnPagerChangedListener.onPagerChanged(this, position);
        }
    }

    private void notifyOnScroll() {
        if (mOnScrollListener != null) {
            mOnScrollListener.onScroll(this, getTotalScrollDistance());
        }
    }

    public int getTotalScrollDistance() {
        int childTotalScrollDistance = 0;
        for (int i = 0; i < getChildCount(); i++) {
            childTotalScrollDistance += getChildAt(i).getScrollY();
        }
        return childTotalScrollDistance + getScrollY();
    }

    public int getCurrentChildIndex() {
        return mCurrentChildIndex;
    }

    public void setOnPagerChangedListener(OnPagerChangedListener onPagerChangedListener) {
        this.mOnPagerChangedListener = onPagerChangedListener;
    }

    public void setOnScrollListener(OnScrollListener onScrollListener) {
        this.mOnScrollListener = onScrollListener;
    }
}
